/*
 * $Id: netio_srv.prg 2012-09-17 14:53 riztan $
 */
/*
 * Harbour Project source code:
 *    demonstration/test code for alternative RDD IO API which uses own
 *    very simple TCP/IP file server.
 *
 * Copyright 2009 Przemyslaw Czerpak <druzus / at / priv.onet.pl>
 * www - http://harbour-project.org
 *
 */

/* \file netio_srv.prg 
 * \brief Archivo Inicial - Servidor NetIO.
 * Inicia un servicio tipo NetIO de Harbour.
 * \details
 * Crea un objeto TPublic que es accesible desde el cliente.
 * Incorpora la posibilidad de crear una conexion a MySQL usando TDolphin.
 *
 * La intención de este programa es ofrecer la posibilidad de crear aplicaciones 
 * livianas que no realicen conexiones directas a la base de datos para reducir
 * la carga en el servdor. 
 * Adicionalmente centralizar las acciones de control en el servidor como tal.
 * Un ejemplo del trabajo podría ser el hecho de generar algo similar a los triggers
 * en el servidor de NetIO de manera que en la aplicación cliente eso sea transparente.
 *
 * \date 2011 
 * \author Riztan Gutierrez <riztan / at / gmail . com>
 *
 */

#define DBSERVER  "localhost"
#define DBPORT    2941
#define DBPASSWD  "topsecret"
#define DBUSER    "riztan"

//#define CRLF   hb_eol()

/** \var oApp       Instancia TPublic
 *  \var oDbServer     Instancia de Conexion a Base de Datos.
 */
static oApp,oDbServer
static hMutex

#include "netio.ch"
#include "common.ch"
#include "hbclass.ch"
#include "tdolphin.ch"

/* funciones db */
//#include "dbtools.prg"

/* fuente de conexion MySQL */
#include "connto.prg"

/* funciones con uso de servicio NetIO en tpuy */
#include "tpy_serv.prg"

/** \brief Procedimiento Princial. */
procedure Main()
   local pSockSrv, lExists
   
   pSockSrv := netio_mtserver( DBPORT,,, /* RPC */ .T., DBPASSWD )
   if empty( pSockSrv )
      ? "Cannot start NETIO server !!!"
      //wait "Press any key to exit..."
      quit
   endif

   ? "Servidor NetIO Iniciado."

   /* Creamos el Objeto Publico */
   oApp := TPublic():New()
   ? "Creado objeto de acceso local [oApp]"

//   oApp:oServer:cConnDefault := ""
//   oApp:oServer := TApp():New()
   ?
   ? "Creando objeto de acceso publico [oApp:oServer]"
   oApp:oServer := DbServer():New("pg", "pg_tpy")


   if !Check_Connection( oApp:oServer )
      return
   endif
   ? "Conexion predeterminada = ", oApp:oServer:cConnDefault

? oApp:oServer:oConnections:pg:cDbType
? oApp:oServer:oConnections:pg:cClass

//  Ejemplo de como lo quiero..
// ? oApp:oServer:Get("ClassName")

   oApp:oServer:GetConnect("mysql", "mysql")
   if !Check_Connection( oApp:oServer:oConnections )
      return
   endif
? oApp:oServer:oConnections:mysql:cClass
//? ValToPrg( oApp:oServer:oConnections:mysql )
   ? "Conexion predeterminada = ", oApp:oServer:cConnDefault

/*
   oApp:oQry := oApp:Query("select * from admconfig.dpmenu","myServer1")
? repl("-",20)
hb_MemoWrit( "log1.txt", hb_valtoexp( oApp:oQry:FillArray() ) )
? repl("-",20)

   oApp:oQry2 := oApp:Query("select * from customer")
? repl("-",20)
//? hb_valtoexp( oApp:oQry2:aData )
hb_MemoWrit( "log2.txt", hb_valtoexp( oApp:oQry2:aData() ) )
? repl("-",20)

return
*/
/*
   oApp:oServer:Add("Server")
   oApp:oServer:Add("cAuthor","Riztan Gutierrez <riztan@gmail.com>")
   oApp:oServer:Add("cCopyright","(c) GPL")
   oApp:oServer:Add("cDbServer","MySQL / TDolphin for " + hb_version() )
*/
?
//   ? "Server: " + oApp:cDbServer
//   ? "Author: " + oApp:cAuthor
?
//   db_Connect( "DbServer", "my_tpy" )

//   db_Connect( "pgServer", "pg_tpy" )
//   db_Connect( "myServer", "my_tpy" )


   hb_idleSleep( 0.1 )
   wait

   ? "NETIO_DISCONNECT():", netio_disconnect( DBSERVER, DBPORT )

   ?
   ? "stopping the server..."
   netio_serverstop( pSockSrv, .t. )

   //oDbServer:End()

return


/** \brief Verifica que la conexión <i>oConnection</i> ha sido creada.
    Igualmente si no hay conexión predeterminada, la asigna.
 */
Function CHECK_CONNECTION( oConnection )

   if !hb_IsObject( oConnection )
      Alert("No se ha podido crear objeto [Connection]")
      return .f.
   else
      if Empty( oApp:oServer:cConnDefault )
         oApp:oServer:SetDefault( oConnection )
      endif
   endif

Return .t.


/** \brief Esto es basado en los ejemplos de netio. 
 *   Cuando se crea el flujo, se crea un hilo..  
 */
function open_DbServer( pConnSock, nCanal, lcheck )
   local cText

   ? CRLF+Repl("_",30)+CRLF+"Ejecutando funcion open_DbServer()"
   
   ? "Creando Conexión a MySQL (Dolphin) Canal:",ALLTRIM(STR(nCanal))

   if hb_ISNIL(oDbServer := ConnectTo() )
      ? "No puede conectar a MySQL"
      return nil
   else
      DBInit()
      
      if ! oDbServer:lError
         cText := "Connection OK" + CRLF
         cText += "Host: " + oDbServer:cHost +CRLF
         cText += "Database: " +oDbServer:cDBName + CRLF

         //? oDbServer:Query( "select * from test")
         //? oDbServer:query( "select get_lock('2',180 )" )
         //? oDbServer:query( "select release_lock('2')" )
         ? cText
      endif
   endif
/*
   ? "puntero: ",oDbServer:hMySQL
*/   
   
   hb_threadDetach( hb_threadStart( @rpc_connect(), pConnSock, nCanal, oDbServer) )

return nCanal



static function rpc_connect( pConnSock, nCanal, oDbServer )

   netio_srvSendItem(pConnSock, nCanal, oDbServer:cHost )
   hb_idleSleep(2)
   netio_srvSendItem(pConnSock, nCanal, oDbServer:cHost )

return nil




/** Envia un mensaje y sus argumentos a un objeto en el servidor.
 * De encontrar el objeto, entrega el mensaje junto con los argumentos 
 * y devuelve lo que el mismo retorna.
 * Es decir, hace de puente entre la aplicacion cliente y on objeto en
 * el servidor NetIO.
 * \return 
 */
function __object( ... )

   local aPubMsgs, cObjId
   local aMsgs
   local lData
   local nPCount
   local cPar, uReturn
   
   if hb_pValue( 1 ) == NIL ; return NIL ; endif

   ? Repl("-",30),CRLF,"FUNCION __object()"
   
   aPubMsgs := __objGetMsgList( oApp )
   
   cObjId := hb_pValue(1)
   default cObjId to ""
   
   /* Verificamos que el objeto esta registrado en oApp */
   if AScan( aPubMsgs, { |element| element = hb_pValue(1) } ) = 0
      ? "elemento ["+ cObjId +"] No encontrado en oApp..."
      return NIL
   endif
// ? "Primer valor = ",hb_pValue(1)
   
   /* Buscamos si el segundo parametro es un Data o Metodo */
   nPCount := PCount()
   
   if nPCount < 2 ; return NIL ; endif
   
   
   lData := iif(nPCount = 2, .t., .f. )
   aMsgs := __objGetMsgList( oApp:Get( cObjId ), lData )
   
   cPar := hb_pValue(2)

   if AScan( aMsgs, { |element| element = Upper( cPar ) } ) = 0
//      ? "["hb_pValue(2),"] No encontrado. "
    
      aMsgs := __objGetMsgList( oApp:Get( cObjId ), !lData )
      if AScan( aPubMsgs, { |element| element = cPar } ) = 0
         ? "["+cPar+"] No encontrado. ",CRLF
         return NIL
      endif
      
   else  // Encontrado
   
      if lData 
         // retornamos el valor del data solicitado.
         //? cPar, ValToPrg(hb_ExecFromArray( oApp:Get( cObjId ) , cPar ))
         /*
         uReturn := hb_ExecFromArray( oApp:Get( cObjId ),;
                                  cPar )
         */
         uReturn := __objSendMsg( oApp:Get( cObjId ), cPar )

         ? "Obj: [",Alltrim( cObjId ),"]   ",;
           "Msg [",cPar,"]  ",CRLF,Repl("=",30),CRLF
         return Value2Remote( uReturn )
      endif
   endif

? "__Object(). Revisar..."
return nil


function __ObjMethod( ... )

   local aPubMsgs, cObjId, cMethod
   local aMsgs, aParams:={}, nParam := 3, cParams
   local lData
   local nPCount
   local uReturn
   local cName

   if hb_pValue( 1 ) == NIL ; return NIL ; endif
   
   aPubMsgs := __objGetMsgList( oApp )

//aeval( aPubMsgs , {|a| qout( a ) } )
   
   cObjId  := hb_pValue(1)
   cMethod := hb_pValue(2)
   default cObjId TO "",  cMethod TO ""
   
   ? Repl("-",30),CRLF,"FUNCION __objMethod()"
   
   /* Verificamos que el objeto esta registrado en oApp */
   if AScan( aPubMsgs, { |element| element = hb_pValue( 1 ) } ) = 0
      ? "elemento ["+ cObjId +"] No encontrado en oApp..."
      return NIL
   endif
// ? "Primer valor = ",hb_pValue(1)
   
   nPCount := PCount()
   
   if nPCount < 2 ; return NIL ; endif
   
   ? " cObjId =>  ", cObjId, " vt ", ValType( cObjId ) 
   aMsgs := __objGetMethodList( oApp:Get( cObjId ) )
   
   while nParam <= nPCount
      AADD( aParams, hb_pValue(nParam) )
      nParam++
   end do
   
//   ? "Total de Parametros reales: ", LEN(aParams)
   ? "Obj: [",Alltrim( cObjId ),"]   ",;
     "Method: [",cMethod,"]  ",CRLF,;
     "Parametros restantes: ",PCOUNT()-2
   if !Empty(aParams)
      ? "---------------------------------------"
      AEVAL( aParams, {|param,n| QOUT("| par",AllTrim(Str(n))," : ",param) } )
      ? "---------------------------------------"
   endif

   if Empty(aParams)
      //uReturn := hb_ExecFromArray( oApp:Get( pValue(1) ), cPar )
      uReturn := __ObjSendMsg( oApp:Get( cObjId ), cMethod )
      
   else
      uReturn := hb_ExecFromArray( oApp:Get( cObjId ),;
                                   cMethod, aParams )
/*                                 
      cParams := ARRAY2CSV(aParams)
      cParams := LEFT(cParams, LEN(cParams)-1)
      cParams := RIGHT(cParams, LEN(cParams)-1)
      
      uReturn := __ObjSendMsg( oApp:Get( pValue(1) ), cPar, &cParams )
*/
   endif
   
   cName := StrTran( AllTrim( Str( Seconds() ) ), ".", "" )
   
   if hb_IsObject( uReturn )
      oApp:Add( cName, uReturn )
      ? "registrado objeto ["+cName+"] en oApp",CRLF,Repl("=",30),CRLF
      return Value2Remote( cName )
   endif
           
//   ? "convirtiendo valor a retornar..."                   
return Value2Remote( uReturn )
     

                              
//eof
